package com.vmock.base.core.response;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.vmock.base.constant.CommonConst;
import com.vmock.base.constant.LoginConst;
import com.vmock.base.utils.ContextUtils;
import com.vmock.base.utils.OutMsgUtils;
import com.vmock.base.utils.SpringUtils;
import com.vmock.biz.entity.Response;
import com.vmock.biz.entity.ResponseRestful;
import com.vmock.biz.entity.Url;
import com.vmock.biz.service.IConfigService;
import com.vmock.biz.service.IResponseRestfulService;
import com.vmock.biz.service.IResponseService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.Serializable;
import java.net.URI;
import java.util.Deque;

import static com.vmock.base.utils.BizUtils.doOutResponse;
import static com.vmock.base.utils.OutMsgUtils.outMsg;
import static java.nio.charset.StandardCharsets.UTF_8;

@Slf4j
public class ProxyStrategy implements IResponseStrategy {

    RestTemplate restTemplate = new RestTemplate();

    /**
     * response service
     */
    private IResponseService responseService = SpringUtils.getBean(IResponseService.class);

    private IConfigService configService = SpringUtils.getBean(IConfigService.class);

    private Cache<String, String> cache;
//    private IResponseRestfulService responseService = SpringUtils.getBean(IResponseRestfulService.class);

//    @Value("${zhisheng.ip:10.237.121.82}")
//    private String server = "10.237.121.82";
//    @Value("${zhisheng.port:80}")
//    private int port = 80;

    public ProxyStrategy() {
        CacheManager cacheManager = SpringUtils.getBean(CacheManager.class);
        this.cache = cacheManager.getCache(CommonConst.CONF_CACHE);
    }

    private String zhishengServer() {
        String ip = cache.get(CommonConst.ZHISHENG_IP);
        if (StringUtils.isNoneBlank(ip)) {
            return ip;
        }
        ip = configService.getVal(CommonConst.ZHISHENG_IP);
        if (StringUtils.isNotBlank(ip)) {
            cache.put(CommonConst.ZHISHENG_IP, ip);
        }
        return ip;
    }

    private int zhishengPort() {
        String port = cache.get(CommonConst.ZHISHENG_PORT);
        if (StringUtils.isNoneBlank(port)) {
            return Integer.parseInt(port);
        }
        port = configService.getVal(CommonConst.ZHISHENG_PORT);
        if (StringUtils.isNotBlank(port)) {
            cache.put(CommonConst.ZHISHENG_PORT, port);
        }
        return Integer.parseInt(port);
    }

    @Override
    public IMockResponse doResponse(Url mockUrl) {
        // get req and resp form context
        HttpServletRequest request = ContextUtils.getRequest();
        HttpServletResponse response = ContextUtils.getResponse();
        // get http method, and convert to int code by {@link org.springframework.http.HttpMethod}
        String method = request.getMethod().toUpperCase();
//        int httpMethodCode = HttpMethod.resolve(method).ordinal();

        // 根据http方法和urlId锁定一条返回
        Response mockResponse = responseService.selectMainResponse(mockUrl.getUrlId());
        if (mockResponse.getStatusCode() != 200) {
            errorReq(response, mockResponse);
        }

        proxy(request, response, mockResponse.getContent());

        return null;
    }

    public void errorReq(HttpServletResponse response, Response mockResponse) {
        // header逻辑处理
        String customHeader = mockResponse.getCustomHeader();
        if (StrUtil.isNotBlank(customHeader)) {
            // 将custom header存储的json 反序列化，并遍历存入header.
            JSONArray jsonArray = new JSONArray(customHeader);
            jsonArray.forEach(jsonItem -> {
                String key = ((JSONObject) jsonItem).getStr("key");
                String val = ((JSONObject) jsonItem).getStr("val");
                response.addHeader(key, val);
            });
        }
        // 默认返回contentType为json
        String contentType = response.getContentType();
        if (StrUtil.isBlank(contentType)) {
            response.setContentType(ContentType.JSON.toString(UTF_8));
        }
        // http code
        response.setStatus(mockResponse.getStatusCode());
        outMsg(mockResponse.getContent(), response);
    }

    @SneakyThrows
    public void proxy(HttpServletRequest request, HttpServletResponse response, String targetUri) {

//        URI uri = new URI("http", null, server, port, targetUri, request.getQueryString(), null);
        URI uri = toTargetUri(request, targetUri);
//
        log.info("proxy to {}", uri);
        String body = IOUtils.toString(request.getReader());
        ResponseEntity<String> responseEntity =
                restTemplate.exchange(uri, HttpMethod.POST, new HttpEntity<String>(body), String.class);

        String respBody = responseEntity.getBody();

        // 默认返回contentType为json
        String contentType = response.getContentType();
        if (StrUtil.isBlank(contentType)) {
            response.setContentType(ContentType.JSON.toString(UTF_8));
        }
        // http code
        response.setStatus(responseEntity.getStatusCode().value());
        outMsg(respBody, response);
    }

    @SneakyThrows
    private URI toTargetUri(HttpServletRequest request,String targetUri) {
        if (!targetUri.startsWith("http")) {
            return new URI("http", null, zhishengServer(), zhishengPort(), targetUri, request.getQueryString(), null);
        }
        if (targetUri.startsWith("https")) {
            throw new IllegalArgumentException("暂不支持https转发");
        }
        URI tempUri = new URI(targetUri);
        return new URI(tempUri.getScheme(), null, tempUri.getHost(), tempUri.getPort(), tempUri.getPath(), request.getQueryString(), null);
    }
}
